﻿using Agile.Infra.Core.Agile.Extensions;
using Agile.Infra.Pay.WxPay.Models;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using System.Net.Http.Headers;
using System.Text;

namespace Agile.Infra.Pay.WxPay
{
    public partial class WxPayService
    {
        /// <summary> 小程序 JSAPI 支付 </summary> 
        public async Task<WxJSAPIPayResponse> JSAPIPayAsync(WxJSAPIPayRequest request)
        {
            CheckRequestParams(request);

            var requestJson = GetWxPrePayModel(request).ToJsonString();

            using (var content = new StringContent(requestJson, Encoding.UTF8, "application/json"))
            {
                var url = "/v3/pay/transactions/jsapi";
                var httpRequestMessage = new HttpRequestMessage(HttpMethod.Post, $"{_wxPayOptions.ServerUrl}{url}");

                // Generate signature
                var timestamp = DateTimeOffset.Now.ToLocalTime().ToUnixTimeSeconds().ToString();
                var nonceStr = Guid.NewGuid().ToString("N");
                var signature = GeneratePrePaySignature("POST", url, timestamp, nonceStr, requestJson, _wxPayOptions.MchCertSecretPath);

                httpRequestMessage.Content = content;
                httpRequestMessage.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
                httpRequestMessage.Headers.Authorization = new AuthenticationHeaderValue("WECHATPAY2-SHA256-RSA2048",
                    GetAuthorization(timestamp, nonceStr, signature));

                var response = await base.HttpRequestAsync(request.OutTradeNo, httpRequestMessage);

                var result = JsonConvert.DeserializeObject<WxPrePayResponse>(response);

                if (result == null || string.IsNullOrWhiteSpace(result.PrePayID))
                {
                    _logger.LogError("Invalid WeChat Pay response: {ResponseContent}", response);
                    throw new PayException("Invalid response from WeChat Pay");
                }

                var res = new WxJSAPIPayResponse
                {
                    NonceStr = Guid.NewGuid().ToString("N"),
                    SignType = "RSA",
                    TimeStamp = DateTimeOffset.Now.ToLocalTime().ToUnixTimeSeconds().ToString(),
                    Package = $"prepay_id={result.PrePayID}"
                };

                // 构建签名串
                var signatureStr = $"{_wxPayOptions.AppID}\n{res.TimeStamp}\n{res.NonceStr}\n{res.Package}\n";

                res.PaySign = GenerateSignature(signatureStr, _wxPayOptions.MchCertSecretPath);

                return res;

            }
        }

        private void CheckRequestParams(WxJSAPIPayRequest request)
        {
            if (request == null)
                throw new ArgumentNullException(nameof(request));

            if (string.IsNullOrWhiteSpace(_wxPayOptions?.AppID))
                throw new InvalidOperationException("AppID is not configured");

            if (string.IsNullOrWhiteSpace(_wxPayOptions?.MchID))
                throw new InvalidOperationException("MchID is not configured");

            if (string.IsNullOrWhiteSpace(_wxPayOptions?.MchCertSecretPath))
                throw new InvalidOperationException("Certificate path is not configured");
        }

        private WxPrePayModel GetWxPrePayModel(WxJSAPIPayRequest request)
        {
            return new WxPrePayModel
            {
                AppID = _wxPayOptions.AppID,
                MchID = _wxPayOptions.MchID,
                Attach = request.Attach,
                Description = request.Description,
                GoodsTag = request.GoodsTag,
                NotifyUrl = request.NotifyUrl,
                OutTradeNo = request.OutTradeNo,
                TimeExpire = request.TimeExpire,
                SupportFapiao = request.SupportFapiao,
                Payer = new WxPayer
                {
                    OpenID = request.OpenID
                },
                SettleInfo = new WxSettleInfo
                {
                    ProfitSharing = request.ProfitSharing
                },
                Amount = new WxPayAmount
                {
                    Total = request.TotalAmount,
                    Currency = "CNY"
                }
            };
        }


        #region 请求实体
        internal class WxPrePayModel
        {
            /// <summary>【公众账号ID】是商户在微信开放平台（移动应用）或公众平台（公众号/小程序）上申请的一个唯一标识。需确保该appid与mchid有绑定关系</summary>
            [JsonProperty("appid")]
            public string AppID { get; set; }

            /// <summary>【商户号】是由微信支付系统生成并分配给每个商户的唯一标识符</summary>
            [JsonProperty("mchid")]
            public string MchID { get; set; }

            /// <summary>【商品描述】必填 string(127) 商品信息描述，用户微信账单的商品字段中可见(可参考JSAPI支付示例说明-账单示意图)，服务商需传递能真实代表商品信息的描述</summary>
            [JsonProperty("description")]
            public string Description { get; set; }

            /// <summary>【商户订单号】必填 string(32) 服务商系统内部订单号，要求6-32个字符内，只能是数字、大小写字母_-|* 且在同一个服务商商户号下唯一</summary>
            [JsonProperty("out_trade_no")]
            public string OutTradeNo { get; set; }

            /// <summary>【支付结束时间】</summary>
            [JsonProperty("time_expire")]
            public string TimeExpire { get; set; }

            /// <summary>【商户数据包】附加数据，在查询API和支付通知中原样返回，可作为自定义参数使用，其总长度限制在128字符以内。</summary>
            [JsonProperty("attach")]
            public string Attach { get; set; }

            /// <summary>【商户回调地址】必填 string(255) 服务商接收支付成功回调通知的地址，需按照notify_url填写注意事项规范填写。</summary>
            [JsonProperty("notify_url")]
            public string NotifyUrl { get; set; }

            /// <summary>【订单优惠标记】订单优惠标记，使用代金券或立减优惠功能时需要的参数</summary>
            [JsonProperty("goods_tag")]
            public string GoodsTag { get; set; }

            /// <summary>【结算信息】</summary>
            [JsonProperty("settle_info")]
            public WxSettleInfo SettleInfo { get; set; }

            /// <summary>【电子发票入口开放标识】 传入true时，支付成功消息和支付详情页将出现开票入口。需要在微信支付服务商平台或微信公众平台开通电子发票功能，传此字段才可生效。</summary>
            [JsonProperty("support_fapiao")]
            public bool SupportFapiao { get; set; }

            /// <summary>【订单金额】必填 订单金额信息</summary>
            [JsonProperty("amount")]
            public WxPayAmount Amount { get; set; }

            /// <summary>【支付者信息】支付者信息</summary>
            [JsonProperty("payer")]
            public WxPayer Payer { get; set; }
        }

        internal class WxSettleInfo
        {
            /// <summary>【分账标识】订单的分账标识在下单时设置，传入true表示在订单支付成功后可进行分账操作</summary>
            [JsonProperty("profit_sharing")]
            public bool ProfitSharing { get; set; }
        }

        internal class WxPayAmount
        {
            /// <summary>【总金额】 订单总金额，单位为分，整型。</summary>
            [JsonProperty("total")]
            public int Total { get; set; }

            /// <summary>【货币类型】符合ISO 4217标准的三位字母代码，固定传：CNY，代表人民币。</summary>
            [JsonProperty("currency")]
            public string Currency { get; set; }
        }

        internal class WxPayer
        {
            /// <summary>【用户标识】用户在商户appid下的唯一标识。下单前需获取到用户的OpenID</summary>
            [JsonProperty("openid")]
            public string OpenID { get; set; }
        }

        #endregion

        #region 响应实体
        internal class WxPrePayResponse
        {
            /// <summary>预支付交易会话标识】预支付交易会话标识，JSAPI或小程序调起支付时需要使用的参数，有效期为2小时，失效后需要重新请求该接口以获取新的prepay_id。</summary>
            [JsonProperty("prepay_id")]
            public string PrePayID { get; set; }
        }
        #endregion
    }
}
